package com.small.servlet;

import com.small.helper.BeanHelper;
import com.small.helper.ConfigHelper;
import com.small.helper.ControllerHelper;
import com.small.helper.LoaderHelper;
import com.small.utils.*;
import com.small.web.Handler;
import com.small.web.ModelAndView;
import com.small.web.Param;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * 请求转发器：所有请求的入口点，是整个MVC框架的核心
 * Created by fangzheng on 2018/1/30.
 */
@WebServlet(urlPatterns = "/*", loadOnStartup = 0)
public class DispatcherServlet extends HttpServlet {
    /**
     * 初始化helper类、注册servlet
     *
     * @param config
     * @throws ServletException
     */
    @Override
    public void init(ServletConfig config) throws ServletException {
        //初始化相关helper类
        LoaderHelper.init();
        //获取servletContext对象，用于注册servlet
        ServletContext servletContext = config.getServletContext();
        //注册处理jsp的servlet
        ServletRegistration jspServlet = servletContext.getServletRegistration("jsp");
        jspServlet.addMapping(ConfigHelper.getAppJspPath() + "*");
        //注册处理静态资源的servlet
        ServletRegistration defaultServlet = servletContext.getServletRegistration("default");
        defaultServlet.addMapping(ConfigHelper.getAppAssetPath() + "*");
    }

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //从request对象中获取请求方法
        String requestMethod = request.getMethod().toLowerCase();
        //从request对象中获取请求路径
        String requestPath = request.getPathInfo();
        //获取请求处理器handler
        Handler handler = ControllerHelper.getHandler(requestMethod, requestPath);
        if (null != handler) {
            //具体的controller类
            Class<?> controllerClass = handler.getControllerClass();
            //具体的controller这个对象
            Object controllerBean = BeanHelper.getBean(controllerClass);

            //封装请求参数
            Param param = this.getParam(request);

            //调用action方法
            Method method = handler.getActionMethod();
            boolean hasParam = false;//是否带参数调用标识   true：是  false：否
            ModelAndView result;
            if (param.getParamMap().isEmpty()){
//                result = (ModelAndView) ReflectionUtils.invokeMethod(controllerBean, method);
//                hasParam = false;
            }else {
                //通过判断action方法是否声明了Param参数来决定是否使用带参数方式调用
                Class<?>[] types = method.getParameterTypes();
                for (Class<?> type : types) {
                    /**
                     * 这里先用Param判断action方法中是否声明了Param这个参数，如果声明了则注入前台传递的参数值
                     * 优化：
                     * 1. 后续可根据action方法中定义的对象，反射获取该对象的成员变量。通过该对象的set方法给成员变量赋值
                     */
                    if ("Param".equals(type.getSimpleName())){
//                        result = (ModelAndView) ReflectionUtils.invokeMethod(controllerBean, method, param);
                        hasParam = true;
                        break;
                    }
                }
            }
            if (hasParam){
                result = (ModelAndView) ReflectionUtils.invokeMethod(controllerBean, method,param);
            }else {
                result = (ModelAndView) ReflectionUtils.invokeMethod(controllerBean, method);
            }

            //处理Action方法的返回值
            if (null != result){
                if (result instanceof ModelAndView) {
                    //返回视图+数据
                    returnView(result, request, response);
                } else {
                    //返回json数据
                    returnData(result, response);
                }
            }
        }
    }

    /**
     * 返回modelAndView
     * @param modelAndView
     * @param request
     * @param response
     * @throws IOException
     * @throws ServletException
     */
    public void returnView(ModelAndView modelAndView, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        String path = modelAndView.getPath();
        if(StringUtil.isNotEmpty(path)){
            if(path.startsWith("/")){
                response.sendRedirect(request.getContextPath()+path);
            }else {
                Map<String,Object> data = modelAndView.getModel();
                for(Map.Entry<String,Object> entry: data.entrySet()){
                    request.setAttribute(entry.getKey(),entry.getValue());
                }
                request.getRequestDispatcher(ConfigHelper.getAppJspPath() + path).forward(request,response);
            }
        }else {
            throw new RuntimeException("未找到视图文件："+path);
        }
    }

    /**
     * 获取请求的所有参数
     * @param request
     * @return
     * @throws IOException
     */
    public Param getParam(HttpServletRequest request) throws IOException {
        Param param = new Param();
        param.addParams(getParamMap(request));
        param.addParams(postParamMap(request));
        return param;
    }
    /**
     * 返回json数据
     * @param modelAndView
     * @param response
     * @throws IOException
     */
    public void returnData(ModelAndView modelAndView, HttpServletResponse response) throws IOException {
        Object model = modelAndView.getModel();
        if (null != model) {
            response.setContentType("application/json");
            response.setCharacterEncoding("UTF-8");
            PrintWriter writer = response.getWriter();
            String json = JsonUtils.toJson(model);
            writer.write(json);
            writer.flush();
            writer.close();
        }
    }

    /**
     * 获取request对象中的请求参数，针对POST类型
     *
     * @param request
     * @return
     */
    public Map<String, Object> postParamMap(HttpServletRequest request) throws IOException {
        String body = CodeUtils.decodeURL(StreamUtils.getString(request.getInputStream()));
        return transforParam(body);
    }

    /**
     * 获取request对象中的请求参数map，针对GET类型
     *
     * @param request
     * @return
     */
    public Map<String, Object> getParamMap(HttpServletRequest request) throws UnsupportedEncodingException {
        return transforParam(request.getQueryString());
    }

    /**
     * 将请求串转换为map对象
     * 例如：name=zhangsan&age=18  转换为Map形式
     * @param paramStr
     * @return
     */
    public Map<String, Object> transforParam(String paramStr) throws UnsupportedEncodingException {
        Map<String, Object> paramMap = new HashMap<>();
        if (null==paramStr || "".equals(paramStr)) return paramMap;

        if (!paramStr.isEmpty()) {
            String[] params = paramStr.split("&");
            if (null != params && params.length > 0) {
                for (String param : params) {
                    String paramCN = toUTF8(param);
                    String[] arrays = paramCN.split("=");
                    paramMap.put(arrays[0], arrays[1]);
                }
            }
        }
        return paramMap;
    }

    /**
     * 将参数转换为UTF-8格式
     * @param str
     * @return
     * @throws UnsupportedEncodingException
     */
    public String toUTF8(String str) throws UnsupportedEncodingException {
        return new String(str.getBytes("ISO8859-1"),"UTF-8");
    }
}
